# Attachments
# by Cee Pee

import forumapi
from forumapi.vbulletin.BBCode import Tag
from deps import BeautifulSoup
import re

__all__ = [
	# Classes
	"Attachment", "AttachTag"
]

## Utilities ##

def _get_attachment_id(url):
	try:
		return(int(forumapi.qs_dict_from_url(url)["attachmentid"][0]))
	except:
		return(None)

def _find_image_attachments(session, soup):
	attachments = []
	
	images = soup.findAll("img", "attach")
	for image in images:
		id = _get_attachment_id(image["src"])
		if id in attachments:
			continue
		
		attachments.append(Attachment(session, id))
	
	thumbs = soup.findAll("img", "thumbnail")
	for thumb in thumbs:
		lines = [line.split(":")[1].strip() for line in thumb["alt"].split("\n")[2:]]
		id = int(lines[3])
		if id in attachments:
			continue
		
		size = session.get_size(lines[2])
		attachments.append(Attachment(session, id, filename=lines[0], size=size))
	
	return(attachments)

## Hooks ##

def _parse_postbit(session=None, soup=None, deleted=None, attachments=[], content=None, content_cell=None, message=None, **kwargs):
	if deleted:
		return
	
	attach_div = message.findNextSibling("div")
	if attach_div is None:
		return
	
	comment = attach_div.previousSibling.previousSibling
	if not (isinstance(comment, BeautifulSoup.Comment) and comment.strip() == "attachments"):
		return
	
	attachments.extend(_find_image_attachments(session, attach_div))
	
	files = attach_div.findAll("tr")
	for file in files:
		link = file.find("a")
		id = _get_attachment_id(link["href"])
		
		attachment = Attachment(session, id, filename=str(link.string))
		attachment.set_file_info(str(link.nextSibling.string).strip())
		attachments.append(attachment)
	
	if hasattr(content, "post_attachments"):
		attachments.extend(content.post_attachments)
		content.post_attachments = []

def _bbcode_init(bbcode=None, session=None, soup=None, **kwargs):
	bbcode.attachments = []
	bbcode.post_attachments = _find_image_attachments(session, soup)
	
	inlines = soup.findAll("a", {"href": lambda url: "attachment.php?attachmentid=" in url})
	for inline in inlines:
		id = _get_attachment_id(inline["href"])
		name = str(inline.string)
		attachment = Attachment(session, id)
		if "." in name:
			attachment._filename = name
			if attachment not in bbcode.post_attachments:
				bbcode.post_attachments.append(attachment)
		elif name == session.lang.phrases["attachment_foreign_link"].format(id=id):
			bbcode.attachments.append(attachment)
	
	bbcode.attachments.extend(bbcode.post_attachments)

def _bbcode_tag_eq(tag=None, other=None, **kwargs):
	if tag.tag == "url" and tag.unparser is not None and tag.unparser.context is not None and hasattr(tag.unparser.context, "attachments"):
		id = _get_attachment_id(other["href"])
		if id is not None and id in tag.unparser.context.attachments:
			return(False)
	
	return(None)

def _bbcode_tags(unparser=None, **kwargs):
	unparser._tags.append(AttachTag("Attachments", "attach", option=False, unparser=unparser))

forumapi.hooks.add("vb_parse_postbit", _parse_postbit)
forumapi.hooks.add("vb_bbcode_init", _bbcode_init)
forumapi.hooks.add("vb_bbcode_tag_eq", _bbcode_tag_eq)
forumapi.hooks.add("vb_bbcode_tags", _bbcode_tags)

## Classes ##

class AttachTag(Tag):
	"""Represents an BBCode inline attachment tag."""
	
	def __eq__(self, other):
		if isinstance(other, BeautifulSoup.Tag):
			if other.name == "img":
				attrs = dict(other.attrs)
				return("class" in attrs and attrs["class"] in ["attach", "thumbnail"])
			elif other.name == "a":
				id = _get_attachment_id(other["href"])
				if id is not None:
					if self.unparser is not None and self.unparser.context is not None and hasattr(self.unparser.context, "attachments"):
						if id in self.unparser.context.attachments:
							self.last_match["value"] = str(id)
							return(True)
						else:
							return(False)
					else:
						return(True)
			else:
				return(False)
		else:
			return(super().__eq__(other))

class Attachment(forumapi.File, forumapi.ExtensibleObject):
	"""It's an attachment. It knows its ID and can retrieve the same data from
	it as other `File` objects.
	
	TODO: Add a post property, make sure it's retrievable and set correctly."""
	
	@classmethod
	def obj(cls, session, *a, **kw):
		if isinstance(a[0], cls):
			return(a[0])
		elif isinstance(a[0], int):
			return(cls(session, *a, **kw))
		else:
			return(None)
	
	def __init__(self, session, id, **kw):
		self.id = id
		
		# For instances tracking, is also stored in File.kw
		self.session = session
		
		super().__init__("attachment.php?attachmentid={0}".format(id), session=session, **kw)
		
		self._find_instances()
	
	def _merge(self, other):
		other.merge(self)
	
	@property
	def data(self):
		data = super().data
		data["id"] = self.id
		data["post"] = int(self.post) if self._post else None
		del data["url"]
		
		return(data)
	
	_file_info_pattern = None
	
	@property
	def file_info_pattern(self):
		if self._file_info_pattern:
			return(self._file_info_pattern)
		else:
			self.__class__._file_info_pattern = re.compile(self.kw["session"].lang.phrases["attachment_file_info"])
			return(self._file_info_pattern)
	
	def set_file_info(self, info):
		if self._size is not None and self.magnitude == 1:
			return
		
		size = self.session.get_size(self.file_info_pattern.match(info).group(1))
		self._size = size[0]
		self.magnitude = size[1]
	
	def __eq__(self, other):
		if isinstance(other, Attachment):
			return(self.id == other.id)
		elif isinstance(other, int):
			return(self.id == other)
		else:
			return(forumapi.File.__eq__(self, other))
	
	def __ne__(self, other):
		return(not self.__eq__(other))
	
	def __int__(self):
		return(self.id)
	
	def __repr__(self):
		return("<vbulletin.Attachment {self.id}{0}{1}>".format(" \"{self.filename}\"".format(self=self) if self._filename else "", " type=\"{self._mime}\"".format(self=self) if self._mime else "", self=self))
